using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Data;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using LiveChartsCore;
using LiveChartsCore.Defaults;
using LiveChartsCore.SkiaSharpView;
using SkiaSharp;
using WPFPractice.Utils;
using WPFPractice.ViewModels;
using WPFTemplateLib.Controls.WpfToast;
using WPFTemplateLib.Models;
using WPFTemplateLib.WpfHelpers;

namespace WPFPractice.Windows
{
	public partial class WinLiveCharts2 : Window
	{
		public WinLiveCharts2()
		{
			InitializeComponent();
			DataContext = new LiveCharts2ViewModel();
		}
	}
}

namespace WPFPractice.ViewModels
{
	public class LiveCharts2ViewModel : MyViewModelBase
	{
		#region 绑定

		public DateTime StartTime { get; set; } = DateTime.Now - TimeSpan.FromHours(1);
		public DateTime EndTime { get; set; } = DateTime.Now;

		public Axis[] XAxes { get; set; }
		public Axis[] YAxes { get; set; }

		public Axis XAxis { get; set; }
		public Axis YAxis { get; set; }

		public Func<double, string> XFormatter { get; set; }
		public Func<double, string> YFormatter { get; set; }

		/// <summary>
		/// 图形集合
		/// </summary>
		public ObservableCollection<ISeries> Series { get; set; } = new();

		/// <summary>
		/// 最大范围（一般设置为全部点的数量，LiveCharts2 好像不能设置）
		/// </summary>
		public double AxisMaxRange { get; private set; }

		/// <summary>
		/// 新建项名称
		/// </summary>
		public string NewItemName { get; set; }

		/// <summary>
		/// 可勾选项的列表
		/// </summary>
		public ObservableCollection<CheckableItem> CheckableItems { get; set; } = new();

		/// <summary>
		/// 选中项
		/// </summary>
		public CheckableItem SelectedItem { get; set; }

		#endregion

		#region 命令

		#region [命令] 新增项
		private RelayCommand _AddNewItemCmd;
		public RelayCommand AddNewItemCmd => _AddNewItemCmd ??= new RelayCommand(ExecuteAddNewItemCmd);
		private void ExecuteAddNewItemCmd()
		{
			if(string.IsNullOrWhiteSpace(NewItemName))
			{
				ToastHelper.ToastToScreenError("请输入新建项名称");
				return;
			}

			CheckableItems.Add(new CheckableItem(NewItemName));
		}
		#endregion

		#region [命令] 移除项
		private RelayCommand _RemoveItemCmd;
		public RelayCommand RemoveItemCmd => _RemoveItemCmd ??= new RelayCommand(ExecuteRemoveItemCmd);
		private void ExecuteRemoveItemCmd()
		{
			if(SelectedItem != null)
			{
				SelectedItem.IsChecked = false;
				CheckableItems.Remove(SelectedItem);
			}
			else
			{
				ToastHelper.ToastToScreenError("请选择一项再进行移除操作");
			}
		}
		#endregion

		#region [命令] 选中项变更事件
		private RelayCommand<SelectionChangedEventArgs> _SelectedItemChangedCmd;
		/// <summary>
		/// [命令] 选中项变更事件
		/// </summary>
		public RelayCommand<SelectionChangedEventArgs> SelectedItemChangedCmd => _SelectedItemChangedCmd ??= new RelayCommand<SelectionChangedEventArgs>(ExecuteSelectedItemChangedCmd);
		private void ExecuteSelectedItemChangedCmd(SelectionChangedEventArgs args)
		{
			foreach(object addItem in args.AddedItems)
			{
				((CheckableItem)addItem).IsSelected = true;
			}

			foreach(object addItem in args.RemovedItems)
			{
				((CheckableItem)addItem).IsSelected = false;
			}
		}
		#endregion

		#region [命令] 生成数据
		private RelayCommand _GenerateDataCmd;
		public RelayCommand GenerateDataCmd => _GenerateDataCmd ??= new RelayCommand(ExecuteGenerateDataCmd);
		private void ExecuteGenerateDataCmd()
		{
			if(SelectedItem == null)
			{
				ToastToScreenWarning("请先选择一项再生成数据");
				return;
			}

			SelectedItem.IsChecked = false;
			PrepareGraphData();
			GenerateGraph();

			//ResetZoomingAndPanning();

			//可解决第一次生成数据时 X轴 不显示的问题;
			Task.Run(async () =>
			{
				XAxis.MaxLimit = EndTime.Ticks + (EndTime.Ticks - StartTime.Ticks) * 0.5;
				await Task.Delay(1000);
				ResetZoomingAndPanning();
			});
		}
		#endregion

		#region [命令] 清除全部图形
		private RelayCommand _ClearAllGraphCmd;
		public RelayCommand ClearAllGraphCmd => _ClearAllGraphCmd ??= new RelayCommand(ExecuteClearAllGraphCmd);
		private void ExecuteClearAllGraphCmd()
		{
			ClearGraph();
		}
		#endregion

		#region [命令] 复位当前缩放和平移
		private RelayCommand _ResetZoomingAndPanningCmd;
		public RelayCommand ResetZoomingAndPanningCmd => _ResetZoomingAndPanningCmd ??= new RelayCommand(ExecuteResetZoomingAndPanningCmd);
		private void ExecuteResetZoomingAndPanningCmd()
		{
			ResetZoomingAndPanning();
		}
		#endregion

		#endregion

		#region 方法

		/// <inheritdoc />
		protected override void InitDataAfterConstruction()
		{
			Console.WriteLine("演示 LiveCharts2 图控件");
			InitGraphStyle();

			CheckableItem defaultItem = new CheckableItem("Default");
			CheckableItems.Add(defaultItem);
			SelectedItem = defaultItem;
		}

		/// <inheritdoc />
		protected override void PropertyChangedHandle(object sender, PropertyChangedEventArgs e)
		{
			base.PropertyChangedHandle(sender, e);
			switch(e.PropertyName)
			{
				case nameof(StartTime):
				case nameof(EndTime):
				{
					if(StartTime >= EndTime)
					{
						ToastHelper.ToastToScreenError("开始时间不能大于或等于结束时间");
						StartTime = EndTime - TimeSpan.FromDays(1);
					}
					break;
				}
			}
		}

		/// <summary>
		/// 初始化图形样式
		/// </summary>
		private void InitGraphStyle()
		{
			//配置模型映射;
			LiveCharts.Configure(config => config.HasMap<DateTimePoint>((model, index) => new(model.DateTime.Ticks, model.Value ?? 0)));

			//配置支持显示中文;
			LiveCharts.Configure(config => config.HasGlobalSKTypeface(SKFontManager.Default.MatchCharacter('汉')));

			YFormatter = value => value.ToString("F2");
			XFormatter = val => FormatDateTime((long)val);

			XAxis = new Axis
			{
				Labeler = XFormatter,
				LabelsRotation = 300,
				//Name = "Time",
				//NamePaint = new SolidColorPaint(SKColors.Black),
				//LabelsPaint = new SolidColorPaint(SKColors.Blue),
				TextSize = 10,
				//SeparatorsPaint = new SolidColorPaint(SKColors.LightSlateGray) { StrokeThickness = 2 },
				UnitWidth = TimeSpan.FromSeconds(1).Ticks, //以秒为单位;
			};
			XAxes = new Axis[] { XAxis };

			YAxis = new Axis() { Labeler = YFormatter, TextSize = 10, };
			YAxes = new Axis[] { YAxis };
		}

		/// <summary>
		/// 格式化时间标签
		/// </summary>
		/// <param name="val"></param>
		/// <returns></returns>
		private string FormatDateTime(long val)
		{
			try
			{
				var data = new DateTime(val);
				return data.ToString("yyyyMMdd HH:mm:ss");
			}
			catch(Exception ex)
			{
				return val.ToString();
			}
		}

		/// <summary>
		/// 横坐标最大值最小值
		/// </summary>
		private void SetAxisLimits()
		{
			long xMin = StartTime.Ticks;
			long xMax = EndTime.Ticks;

			var datas = SelectedItem?.Data as List<DateTimePoint>;
			if(datas != null && datas.Any())
			{
				DateTime min = datas.Min(x => x.DateTime);
				DateTime max = datas.Max(x => x.DateTime);
				xMin = min.AddMinutes(-1).Ticks;
				xMax = max.AddMinutes(1).Ticks;

				AxisMaxRange = datas.Count;
			}

			XAxis.MinLimit = xMin;
			XAxis.MaxLimit = xMax;

			double separatorCount = 55.0;
			XAxis.MinStep = (xMax - xMin) / separatorCount;
		}

		/// <summary>
		/// 准备图形数据
		/// </summary>
		public void PrepareGraphData()
		{
			if(SelectedItem == null)
				return;

			List<DateTimePoint> list = new();
			DateTime time = StartTime;
			Random random = Random.Shared;
			while(time < EndTime)
			{
				DateTimePoint point = new(time, random.Next(0, 100));
				list.Add(point);

				time += TimeSpan.FromSeconds(1);
			}

			SelectedItem.Data = list;
			SelectedItem.CheckStatusChanged -= CheckItemCheckChanged;
			SelectedItem.CheckStatusChanged += CheckItemCheckChanged;

			ToastToScreen($"已填充 {list.Count} 条数据");
		}

		/// <summary>
		/// 生成图形
		/// </summary>
		public void GenerateGraph()
		{
			//ClearGraph();

			//SetAxisLimits();

			//foreach(CheckableItem item in CheckableItems)
			//{
			//	CheckItemCheckChanged(item);
			//	item.CheckStatusChanged -= CheckItemCheckChanged;
			//	item.CheckStatusChanged += CheckItemCheckChanged;
			//}

			if(SelectedItem != null)
			{
				SelectedItem.IsChecked = true;
			}
		}

		/// <summary>
		/// 勾选项勾选变更
		/// </summary>
		/// <param name="item"></param>
		public void CheckItemCheckChanged(CheckableItem item)
		{
			try
			{
				string title = string.IsNullOrWhiteSpace(item.GroupName)
					? item.ShowName
					: $"{item.GroupName}_{item.ShowName}";

				bool isChecked = item.IsChecked;
				if(isChecked)
				{
					LineSeries<DateTimePoint> lineSeries = new()
					{
						Name = title,
						//Mapping = (model, index) => new(model.DateTime.Ticks, model.Value ?? 0), //模型映射，已全局配置;
						Fill = null, //线条下方与 X 轴围成区域的颜色;
						LineSmoothness = 0,     //线条平滑度;
						GeometryFill = null,    //点的填充;
						GeometrySize = 2,       //点的大小;
						//Stroke = new SolidColorPaint(SKColors.LightBlue) { StrokeThickness = 3 }, //连线的颜色等;
						//DataPadding = new LvcPoint(2, 2), //数据与坐标轴的距离;
					};

					if(item.Data is List<DateTimePoint> list)
					{
						list = list.OrderBy(x => x.DateTime).ToList();

						Dispatcher?.Invoke(() =>
						{
							lineSeries.Values = list;
						});
					}

					Series.Add(lineSeries);
				}
				else
				{
					var series = Series.FirstOrDefault(n => n.Name == title);
					if(series != null)
					{
						// Try/catch necessary for unit tests
						// Erase throws a NullReferenceException internally if no view
						try
						{
							//series.Erase(true);
							//series.SoftDeleteOrDispose();
							Series.Remove(series);
						}
						catch
						{
							// ignored
						}

						//series.Values.Clear();
					}
				}
			}
			catch(Exception ex)
			{
				Console.WriteLine($"{ex}");
				Dispatcher?.Invoke(() =>
				{
					ToastHelper.ToastToScreenError(ex.Message);
				});
			}
		}

		/// <summary>
		/// 清空图形
		/// </summary>
		public void ClearGraph()
		{
			Dispatcher?.Invoke(() =>
			{
				Series.Clear();
			});
		}

		/// <summary>
		/// 复位缩放和平移
		/// </summary>
		private void ResetZoomingAndPanning()
		{
			XAxis.MinLimit = null;
			XAxis.MaxLimit = null;

			YAxis.MinLimit = null;
			YAxis.MaxLimit = null;
		}

		#endregion
	}
}
